package com.godenwater.recv.handler;

import cn.gov.mwr.sl651.utils.ByteUtil;
import com.godenwater.recv.decode.YanyuMessageDecoder;
import com.godenwater.recv.model.CommonMessage;
import com.godenwater.recv.server.hd.HdDownCommand;
import com.godenwater.recv.server.hd.HdMessageHeader;
import com.godenwater.recv.utils.ByteUtils;
import com.godenwater.yanyu.YYBuilder;
import com.godenwater.yanyu.YYParser;
import com.godenwater.recv.RecvConstant;
import com.godenwater.recv.server.all.RtuServer;
import org.apache.commons.lang3.StringUtils;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * 宏电协议的处理器，此协议可以理解为燕禹协议的加强版
 *
 * @author lipujun
 */
public class HdMessageHandler extends AbstractHandler {

    private static Logger logger = LoggerFactory
            .getLogger(HdMessageHandler.class);

    /**
     * Returns a singleton HdMessageHandler instance.
     *
     * @return a HdMessageHandler instance.
     */
    public static HdMessageHandler getInstance() {
        return HdMessageHandlerContainer.instance;
    }

    // Wrap this guy up so we can mock out the UserManager class.
    private static class HdMessageHandlerContainer {
        private static HdMessageHandler instance = new HdMessageHandler();
    }

    private HdMessageHandler() {
    }

    public void perform(String channel, IoSession session, CommonMessage message, int recvPort) {
        byte funcCode;
        byte[] dtuCode;
        boolean crcFlag = true;

        HdMessageHeader header = (HdMessageHeader) message.getHeader();
        funcCode = header.getCMD_TYPE();
        dtuCode = header.getDTU_ID();
        String tel = new String(dtuCode);

        // 3、应答回复或重发报文请求
        byte[] replyMsg = null;

        switch (funcCode) {
            //case (byte)0x81://注册包，可以理解为心跳包，必须回复
            case (byte) 0x01://注册包，可以理解为心跳包，必须回复
                replyMsg = HdDownCommand.cmd81DTU(dtuCode);
                break;
            case (byte) 0x02:
                replyMsg = HdDownCommand.cmd82DTU(dtuCode);
                break;
            case (byte) 0x09://数据包，包含了数据内容，必须回复
                //获取宏电模块中内容的部分，并得到回复消息内容
                byte[] content = message.getContent();//此部分内容为加载燕禹68字节协议部分的内容
                CommonMessage cm = YanyuMessageDecoder.decode(content);
                // 1、 校验消息
                crcFlag = checkCRC(cm);// 校验消息体，CRC校验
                //需要进行转换，并获取相对应的内容
                byte[] rpy_content = yyAdapter(session, cm, crcFlag);
                logger.debug("回复报文，" + tel + " " + ByteUtil.toHexString(rpy_content) + "");
                replyMsg = HdDownCommand.cmd89DTU(channel, dtuCode, rpy_content);
                break;
        }

        if (replyMsg != null && (channel.equalsIgnoreCase("GPRS") || channel.equalsIgnoreCase("TCP") || channel.equalsIgnoreCase("UDP"))) {//
            logger.debug("回复报文，" + tel + " " + ByteUtil.toHexString(replyMsg) + "");
            logger.info("S>回复报文 " + tel + " " + ByteUtil.toHexString(replyMsg) + "");
            session.write(replyMsg);
        }
        RtuServer.getInstance().getSessionManager().bindHdSession(session, tel);
        // 6、前台监测通知，此处查看的是包含宏电模块内容，此处会有心跳包，会引起报文监测显示频繁
        String logMsg = viewMessage(channel, message);
        monitorMessage(channel, tel, logMsg);

        String ip = "";
        Integer port = 0;
        try {
            ip = ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress();
            port = ((InetSocketAddress) session.getRemoteAddress()).getPort();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //仅对属于有数据的报文内容进行显示
        switch (funcCode) {
            case (byte) 0x09:
                //绑定带数据的宏电内容
                byte[] bytes = ByteUtils.hexStringToBytes(ByteUtil.toHexString(message.getContent()));
                CommonMessage yyMessage = YanyuMessageDecoder.decode(bytes);
                com.godenwater.yanyu.IMessageHeader title = (com.godenwater.yanyu.IMessageHeader) yyMessage.getHeader();
                byte[] stationAddr = title.getStationAddr();
                byte[] centerAddr = title.getCenterAddr();
                String stcd = "" + YYParser.parseStcd(centerAddr, stationAddr);
                RtuServer.getInstance().getSessionManager().bindHdSession(session, tel, stcd, ByteUtil.toHexString(centerAddr), ByteUtil.toHexString(stationAddr));
                // 7、 写入报文记录,因为里面的是燕禹的报文，直接存为燕禹的报文内容来解析
                saveMessage(channel, RecvConstant.YY, tel, crcFlag, ByteUtil.toHexString(message.getContent()), "1", ByteUtil.toHexString(replyMsg), ip, port, ByteUtil.toHexString(funcCode), 0);
                break;

        }

    }

    public byte[] yyAdapter(IoSession session, CommonMessage cm, boolean crcFlag) {

        byte[] replyMsg;

        if (crcFlag) {
            // 确认应答
            logger.info("校验宏电报文内容，报文CRC验证<相一致>，M2模式发送\"应答\"回复....");
            replyMsg = replyMessage(session, cm);
        } else {
            // 重发请求
            logger.info("校验宏电报文内容，报文CRC验证\"不一致 \"，M2模式发送\"重发\"请求....");
            replyMsg = repeatMessage(cm);
        }

        return replyMsg;
    }

    /**
     * 宏电模块中，无crc校验，但需要校验报文中的内容
     *
     * @param message
     * @return
     */
    public boolean checkCRC(CommonMessage message) {
        com.godenwater.yanyu.IMessageHeader header = (com.godenwater.yanyu.IMessageHeader) message
                .getHeader();
        byte[] body = message.getContent();

        //logger.info("校验报文，开始校验宏电报文数据的CRC...  ");

        byte[] bytes = new byte[header.getLength() + body.length + 1];

        int pos = 0;
        System.arraycopy(header.getStartBit(), 0, bytes, pos,
                header.getStartBit().length);
        pos = pos + header.getStartBit().length; // 此处将取值直接改为取数据中字节的长度，对字节处理更精确

        System.arraycopy(header.getCenterAddr(), 0, bytes, pos,
                header.getCenterAddr().length);
        pos = pos + header.getCenterAddr().length;

        System.arraycopy(header.getStationAddr(), 0, bytes, pos,
                header.getStationAddr().length);
        pos = pos + header.getStationAddr().length;

        System.arraycopy(header.getFuncCode(), 0, bytes, pos,
                header.getFuncCode().length);
        pos = pos + header.getFuncCode().length;

        System.arraycopy(header.getBodySize(), 0, bytes, pos,
                header.getBodySize().length);
        pos = pos + header.getBodySize().length;

        System.arraycopy(body, 0, bytes, pos, body.length);
        pos = pos + body.length;

        // System.arraycopy(message.getEOF(), 0, bytes, pos, 1);
        bytes[pos] = message.getEOF();

        byte[] crcResult = com.godenwater.yanyu.utils.CRC16Helper.crc16Check(bytes);

        // System.out.println(">> crcResult " +
        // ByteUtil.toHexString(crcResult));

        if (Arrays.equals(crcResult, message.getCRC())) {
            return true;
        } else {
            return false;
        }
    }

    public byte[] replyMessage(IoSession session, CommonMessage message) {
        try {
            com.godenwater.yanyu.IMessageHeader header = (com.godenwater.yanyu.IMessageHeader) message.getHeader();

            // 报文回复
            byte[] funcCode = header.getFuncCode();

            com.godenwater.yanyu.command.DownCommand cmd = new com.godenwater.yanyu.command.DownCommand();
            cmd.setStartBit(header.getStartBit()[0]);
            cmd.setCenterAddr(header.getCenterAddr());
            cmd.setStationAddr(header.getStationAddr());
            cmd.setFuncCode(funcCode);
            //设置时间
            SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss");
            String tm = sdf.format(new Date());
            //byte[] tmCode = ByteUtil.HexStringToBinary("09" + tm);
            byte xstcd1 = (byte) (header.getCenterAddr()[0] & 0x01);//仅借一位
            int xstcd = com.godenwater.yanyu.utils.ByteUtil.bytesToUshort(new byte[]{xstcd1, header.getStationAddr()[0]});
            RtuServer.getInstance().getSessionManager().bindCaller(session, String.valueOf(xstcd), "1");//默认认为有下发命令
            logger.info("S>绑定下发测站 " + xstcd + "！");
            // 2、判断结束符是哪种格式
            // 2.1 ：在报文分包传输时作为结束符，表示未完成，不可退出通信
            if (message.getEOF() == com.godenwater.yanyu.Symbol.ETB) {
                // ACK 肯定确认，继续发送，作为有后续报文帧的“确认”结束符。
                // NAK 否定应答，反馈重发， 用于要求对方重发某数据包的报文结束符。
                // ENQ 作为下行查询及控制命令帧的报文结束符。

                cmd.setEof(com.godenwater.yanyu.Symbol.ACK);
                //水位雨量固态数据提取保温回复不需要时间
                if (funcCode[0] == 0x06 || funcCode[0] == 0x0a) {
                    return YYBuilder.toByte(cmd.sendAckMessage(false), cmd.getUpDown());
                } else {
                    return YYBuilder.toByte(cmd.sendAckMessage(true), cmd.getUpDown());
                }

            }

            // 2.2：作为报文结束符，表示传输完成等待退出通信，
            if (message.getEOF() == com.godenwater.yanyu.Symbol.ETX) {

                // --- 反馈用EOT 作为传输结束确认帧报文符，表示可以退出通信。
                // --- ESC 要求终端在线。保持在线10分钟内若没有接收到中心站命令，终端退回原先设定的工作状态
                cmd.setEof(com.godenwater.yanyu.Symbol.EOT);

                // 判断是否有召测信息，如果有，则需要将遥测站保持在线10分钟，见协议：6.2.2（2017-06-10调整为直接查找测站，燕禹的测站编码有借位的现象）此处需要调整
                //byte xstcd1 = (byte)(header.getCenterAddr()[0] & 0x0F);
                //int xstcd = com.easywater.yanyu.utils.ByteUtil.bytesToUshort(new byte[]{xstcd1,header.getStationAddr()[0]});
                int downCmdLen = checkYYCommand("YY", String.valueOf(xstcd));//这个需要改变为，燕禹的协议有借位的现象
                if (downCmdLen > 0) {
                    cmd.setEof(com.godenwater.yanyu.Symbol.ESC);
                    // 表示有下发的报文，需要进行下发
                    logger.info("S>校验有测站号为 " + xstcd + " 的下发命令!");
                    RtuServer.getInstance().getSessionManager().bindCaller(session, String.valueOf(xstcd), "1");
                }
            }

            //2.3、如果结尾都是错的，默认用一个正确结尾（2017-06-10修改）
            if (message.getEOF() != com.godenwater.yanyu.Symbol.ETX && message.getEOF() != com.godenwater.yanyu.Symbol.ETB) {
                cmd.setEof(com.godenwater.yanyu.Symbol.EOT);//默认用一个正确结尾
                logger.info("S>上报报文结束符不正确，结束符为： " + com.godenwater.yanyu.utils.ByteUtil.byteToHexString(new byte[]{message.getEOF()}) + " ！");
                //2、如果结尾都是错的，我们都检查下发报文
                //byte xstcd1 = (byte)(header.getCenterAddr()[0] & 0x0F);
                // int xstcd = com.easywater.yanyu.utils.ByteUtil.bytesToUshort(new byte[]{xstcd1,header.getStationAddr()[0]});
                int downCmdLen = checkYYCommand("YY", String.valueOf(xstcd));//这个需要改变为，燕禹的协议有借位的现象
                if (downCmdLen > 0) {
                    cmd.setEof(com.godenwater.yanyu.Symbol.ESC);
                    // 表示有下发的报文，需要进行下发
                    logger.info("S>校验有测站号为 " + xstcd + " 的下发命令！");
                    RtuServer.getInstance().getSessionManager().bindCaller(session, String.valueOf(xstcd), "1");
                }
            }
            //水位雨量固态数据提取保温回复不需要时间
            if (funcCode[0] == 0x06 || funcCode[0] == 0x0a) {
                return YYBuilder.toByte(cmd.sendReplyMessage(false), cmd.getUpDown());
            } else {
                return YYBuilder.toByte(cmd.sendReplyMessage(true), cmd.getUpDown());
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.info(">> 构造回复报文出现异常！" + e.getMessage());
            return null;
        }
    }


    /**
     * 构造重发报文
     *
     * @return
     */
    public byte[] repeatMessage(CommonMessage message) {

        try {
            com.godenwater.yanyu.IMessageHeader header = (com.godenwater.yanyu.IMessageHeader) message
                    .getHeader();

            // 报文回复
            byte[] funcCode = header.getFuncCode();

            com.godenwater.yanyu.command.DownCommand cmd = new com.godenwater.yanyu.command.DownCommand();
            cmd.setStartBit(header.getStartBit()[0]);
            cmd.setCenterAddr(header.getCenterAddr());
            cmd.setStationAddr(header.getStationAddr());
            cmd.setFuncCode(funcCode);
            cmd.setEof(com.godenwater.yanyu.Symbol.NAK);

            return YYBuilder.toByte(cmd.sendReplyMessage(true), cmd.getUpDown());

        } catch (Exception e) {
            e.printStackTrace();
            logger.info(">> 构造重发报文出现异常！" + e.getMessage());
            return null;
        }
    }

    public String viewMessage(String channel, CommonMessage message) {
        String log = "";
        try {
            HdMessageHeader header = (HdMessageHeader) message.getHeader();
            byte[] body = message.getContent();
            byte[] bytes;
            if (body == null) {//有内容为空的现象
                bytes = new byte[header.getLength()];
            } else {
                bytes = new byte[header.getLength() + body.length];
            }
            int pos = 0;
            bytes[0] = 0x7B;
            bytes[1] = header.getCMD_TYPE();
            System.arraycopy(header.getBODY_LEN(), 0, bytes, 2,
                    header.getBODY_LEN().length);
            pos = 2 + header.getBODY_LEN().length;

            System.arraycopy(header.getDTU_ID(), 0, bytes, pos,
                    header.getDTU_ID().length);
            pos = pos + header.getDTU_ID().length;

            if (header.getCMD_TYPE() == 0x01) {
                System.arraycopy(header.getIP(), 0, bytes, pos,
                        header.getIP().length);
                pos = pos + header.getIP().length;

                System.arraycopy(header.getPORT(), 0, bytes, pos,
                        header.getPORT().length);
                pos = pos + header.getPORT().length;

                bytes[pos] = 0x7b;
            }


            if (header.getCMD_TYPE() == 0x09) {
                //udp方式为先加7B结束符
                if (StringUtils.equalsIgnoreCase(channel, "udp")) {
                    bytes[pos] = 0x7b;
                    pos = pos + 1;

                    if (body != null) {
                        System.arraycopy(body, 0, bytes, pos, body.length);
                        pos = pos + body.length;
                    }
                }

                //tcp方式为，先加数据内容，后加结束符
                if (StringUtils.equalsIgnoreCase(channel, "tcp") || StringUtils.equalsIgnoreCase(channel, "gprs")) {
                    if (body != null) {
                        System.arraycopy(body, 0, bytes, pos, body.length);
                        pos = pos + body.length;
                    }
                    bytes[pos] = 0x7b;
                }
            }
            //与类型有关是标识是09
            //bytes[bytes.length-1]=

            log = ByteUtil.toHexString(bytes);
            return log;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return log;
    }

    public static void main(String[] args) {
        byte xstcd1 = (byte) (0xFE & 0x01);//仅借一位
        byte xstcd2 = 0x19;
        int xstcd = com.godenwater.yanyu.utils.ByteUtil.bytesToUshort(new byte[]{xstcd1, xstcd2});
        System.out.println(">> xstcd " + xstcd);


        String tel = "13811969446";
        System.out.println(">> tel " + ByteUtil.byteToHexString(tel.getBytes()));


        String cfg = "<53><cfg>";
        System.out.println(">> cfg " + ByteUtil.byteToHexString(cfg.getBytes()));

    }

}
